
package redeS.p2pwsrep;

import java.util.ArrayList;
import java.util.Hashtable;

import peersim.config.Configuration;
import peersim.core.GeneralNode;
import peersim.core.Node;

/**
 * Nó que admite Reputação.
 * @author thiago-pc
 */
public class P2PWSRepNode extends GeneralNode {

	public static String PAR_NFILES = "number_of_files";
	private static int defaultNumberOfFiles = 10;
	public static int numberOfFiles;
	
	
	
	/** Reputação do nó em si - essa reputação só é conhecida internamente */
	public double reputation;
	
	/** Reputação dos arquivos desse nó - só é conhecida internamente */
	public double[] reputationFile;
	
	/** Média da reputação dos arquivos desse nó - só é conhecida internamente*/
	public double reputationFileAvg;
	
	/** Lista dos nós descobertos */
	private ArrayList<P2PWSRepNode> discoveredNodes;
	
	/** Número de transações satisfatórias com um nó */
	private Hashtable<P2PWSRepNode, Integer> satTrans;
	
	/** Número de transações insatisfatórias com um nó */
	private Hashtable<P2PWSRepNode, Integer> unsatTrans;
	
	/** Coloque as reputações calculadas sobre os outros nós nessa tabela */
	public Hashtable<P2PWSRepNode, Double> reputationTable;
	
	private String prefix = null;
	
	public P2PWSRepNode(String prefix) {
		super(prefix);
		if(prefix == null) this.prefix = prefix;
		
		numberOfFiles = Configuration.getInt(prefix + "." + PAR_NFILES, defaultNumberOfFiles);
		reputationFile = new double[numberOfFiles];
		
		double sum = 0;
		reputation = Math.random();
		for(int i=0; i<numberOfFiles; i++){
			reputationFile[i] = Math.random();
			sum += reputationFile[i];
		}
		reputationFileAvg = sum / numberOfFiles;
		
		discoveredNodes = new ArrayList<P2PWSRepNode>();
		satTrans = new Hashtable<P2PWSRepNode, Integer>();
		unsatTrans = new Hashtable<P2PWSRepNode, Integer>();
		reputationTable = new Hashtable<P2PWSRepNode, Double>();
	}
	
	/**
	 * Nós descobertos através da mensagem QueryReputation. Ao receber um
	 * QueryReputation deve-se usar esse método na referência do pai da mensagem passando
	 * como parâmetro o nó que recebe a mensagem.:
	 * queryReputation.getFather().addDiscoveredNode(this)
	 * @param node - normalmente esse parâmetro é <b>this</b>, o nó usado na simulação
	 * deve ser, obrigatoriamente, um {@link P2PWSRepNode}.
	 */
	public void addDiscoveredNode(Node node){
		if(!discoveredNodes.contains(node) && node != this){
			discoveredNodes.add((P2PWSRepNode)node);
		}
	}
	
	/**
	 * Retorna os nós conhecidos por esse nó. Use somente para consulta
	 * @return
	 */
	public ArrayList<P2PWSRepNode> getDiscoveredNodes() {
		return discoveredNodes;
	}
	
	/**
	 * Adiciona uma transação satisfatória entre esse nó e <b>node</b>
	 * @param node - nó da rede ao qual a transação satisfatória se aplica
	 */
	public void addSatTrans(P2PWSRepNode node){
		if(satTrans.containsKey(node)){
			satTrans.put(node, satTrans.get(node) + 1);
		}
		else{
			satTrans.put(node, 1);
		}
	}
	
	/**
	 * Adiciona uma transação insatisfatória entre esse nó e <b>node</b>
	 * @param node - nó da rede ao qual a transação insatisfatória se aplica
	 */
	public void addUnsatTrans(P2PWSRepNode node){
		//boolean debug = node == null;
		if(unsatTrans.get(node) != null){
			//System.out.println(unsatTrans.get(node) + " " + debug); //
			unsatTrans.put
			(node,
					unsatTrans.get(node)
					+ 1);
		}
		else{
			unsatTrans.put(node, 1);
		}
	}
	
	/**
	 * Retorna o número de transações satisfatórias entre esse nó e <b>node</b>
	 * @param node
	 */
	public int getSatTrans(P2PWSRepNode node){
		try{
			return satTrans.get(node);
		}
		catch(NullPointerException e){
			return 0;
		}
	}
	
	/**
	 * Retorna o número de transações insatisfatórias entre esse nó e <b>node</b>
	 * @param node
	 */
	public int getUnsatTrans(P2PWSRepNode node){
		try{
			return unsatTrans.get(node);
		}
		catch(NullPointerException e){
			return 0;
		}
	}
	
	/**
	 * Pega a próxima transação com esse nó.</br>
	 * A probabilidade do retorno (sucesso=true | falha=false) é proporcional à reputação
	 * desse nó.
	 * @param file - recurso a ser buscado
	 */
	public boolean getNextTrans(int file){
		return Math.random() < (reputation * reputationFile[file]);
	}
	
	@Override
	public Object clone(){
		return new P2PWSRepNode(prefix);
	}
	
	@Override
	public String toString(){
		return " " + getID();
	}
}
